/*
 * ClassGenerator.java
 *
 * Created on April 8, 2010, 9:28 AM
 *
 */

package com.absisgroup.nsc.utilities;

import java.io.FileOutputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Properties;
/**
 *
 * @author Hue
 */
public class EntityGenerator {
    private final Properties convertion;
    protected final Properties imports;

    public EntityGenerator() {
        convertion = new Properties();
        convertion.put("PRIMARY_KEY", "Long");
        convertion.put("FOREIGN_KEY", "Long");
        convertion.put("NUMBER", "BigDecimal");
        convertion.put("DATE", "Timestamp");

        convertion.put("VARCHAR2", "String");
        convertion.put("VARCHAR", "String");
        convertion.put("CHAR", "String");

        convertion.put("FLOAT", "float");
        convertion.put("INTEGER", "int");

        convertion.put("BLOB", "byte[]");
        convertion.put("CLOB", "byte[]");
        convertion.put("BFILE", "byte[]");
        convertion.put("LONG", "byte[]");
        convertion.put("LONGRAW", "byte[]");

        imports = new Properties();
        imports.put("BigDecimal", "import java.math.BigDecimal;");
        imports.put("Timestamp", "import java.sql.Timestamp;");
    }

    public static void main(final String[] args) throws Exception {
        final String tableName = args[0];
        final String packageName = args[1];
        final String author = args[2];
        final Connection con = getConnection();
        new EntityGenerator().generate(con, tableName, packageName, author);
        con.close();
    }
    
    public byte[] generate(final Connection con, final String tablename, final String packageName, final String author) throws Exception {
        final String primaryKey = getPrimaryKey(con, tablename);
        final ArrayList alColumns = getColumns(con, tablename);
        if(alColumns.isEmpty()) {
            System.out.println("Table " + tablename + " does not exist.");
            return null;
        }
        else if(primaryKey == null || "".equals(primaryKey)) {
            return "primaryKey not found! primaryKey must ends with _PK!".getBytes();
        }
        final StringBuffer sb = new StringBuffer();
        final SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy hh:mm a");
        final String className = columnToFunction(tablename) + "Entity";
        final StringBuffer sbImports = new StringBuffer("import com.absisgroup.util.Entity;\r\n");
        final String properties = generateProperties(primaryKey, alColumns, sbImports);
        sb.append("/*\r\n");
        sb.append(" * ").append(className).append(".java\r\n");
        sb.append(" *\r\n");
        sb.append(" * Generated on ").append(df.format(new java.util.Date())).append("\r\n");
        sb.append(" * DO NOT MODIFY THIS CLASS!!!. It's generated automatically.\r\n");
        sb.append(" */\r\n\r\n");

        sb.append("package ").append(packageName).append(";\r\n\r\n");
        sb.append(sbImports);
        sb.append("/**\r\n");
        sb.append(" *\r\n");
        sb.append(" * @author ").append(author).append("\r\n");
        sb.append(" */\r\n");
        sb.append("public class ").append(className).append(" extends Entity {\r\n");
        sb.append(properties);
        sb.append("\r\n");
        sb.append(mappingInfo(className, tablename, primaryKey));
        sb.append(generateMethods(primaryKey, alColumns));
        sb.append('}');
        //storeFile("C:/" + className + ".java", sb.toString().getBytes());
        return sb.toString().getBytes();
    }

    protected void storeFile(final String filename, final byte data[]) throws Exception {
        final FileOutputStream out = new FileOutputStream(filename);
        out.write(data);
        out.close();
    }

    protected String convertDataType(final String oracleDataType) {
        final String javaDataType = convertion.getProperty(oracleDataType);
        return (javaDataType == null ? "String" : javaDataType);
    }

    protected String columnToProperty(String column) {
        column = columnToFunction(column);
        return Character.toLowerCase(column.charAt(0)) + column.substring(1);
    }

    protected String columnToFunction(String column) {
        column = column.toLowerCase();
        column = column.substring(0, 1).toUpperCase() + column.substring(1);
        for(int iIndex = 0; (iIndex = column.indexOf("_", iIndex)) != -1;) {
            final String strHeader = column.substring(0, iIndex);
            final String strContent = column.substring(iIndex + 1, iIndex + 2);
            final String strFooter = column.substring(iIndex + 2);
            column = strHeader + strContent.toUpperCase() + strFooter;
        }

        return column;
    }

    private String generateProperties(final String primaryKey, final ArrayList alColumnsTmp, final StringBuffer sbImports) {
        final ArrayList alColumns = new ArrayList();
        alColumns.add(new String[] {primaryKey, "PRIMARY_KEY"});
        alColumns.addAll(alColumnsTmp);

        return generateProperties(alColumns, sbImports);
    }

    protected String generateProperties(final ArrayList alColumns, final StringBuffer sbImports) {
        final ArrayList alTmp = new ArrayList();
        String properties = "";
        for(int i=0; i<alColumns.size(); i++) {
            final String[] col = (String[]) alColumns.get(i);
            final String oracleDataType = col[0].toLowerCase().endsWith("_fk") ? "FOREIGN_KEY" : col[1];
            final String dataType = convertDataType(oracleDataType);
            properties += "    private " + dataType + " " + columnToProperty(col[0]) + ";\r\n";
            final String imp = imports.getProperty(dataType);
            if(imp != null && !alTmp.contains(imp)) {
                alTmp.add(imp);
            }
        }
        for(int i=0; i<alTmp.size(); i++) {
            sbImports.append(alTmp.get(i));
            sbImports.append("\r\n");
        }
        return properties;
    }

    private String generateMethods(final String primaryKey, final ArrayList alColumnsTmp) {
        final ArrayList alColumns = new ArrayList();
        alColumns.add(new String[] {primaryKey, "PRIMARY_KEY"});
        alColumns.addAll(alColumnsTmp);
        
        return generateMethods(alColumns, false);
    }

    protected String generateMethods(final ArrayList alColumns, final boolean bIsCustomEntity) {
        String methods = "";
        for(int i=0; i<alColumns.size(); i++) {
            final String[] col = (String[]) alColumns.get(i);
            final String function = columnToFunction(col[0]);
            final String property = columnToProperty(col[0]);
            final String oracleDataType = col[0].toLowerCase().endsWith("_fk") ? "FOREIGN_KEY" : col[1];
            final String dataType = convertDataType(oracleDataType);
            methods += "\r\n    public void set" + function + "(" + dataType + " value) {\r\n" +
                       "        " + property + " = value;\r\n" +
                       (bIsCustomEntity ? "" : ("PRIMARY_KEY".equals(col[1]) ? "" : "        setModified(\"" + col[0] + "\");\r\n")) +
                       "    }\r\n" +
                       "\r\n" +
                       "    public " + dataType + " get" + function + "() {\r\n" +
                       "        return " + property + ";\r\n" +
                       "    }\r\n";
        }
        return methods;
    }

    private String mappingInfo(final String className, final String tablename, final String primaryKey) {
        return "    public " + className + "() {\r\n" +
               "        super(\"" + tablename.toUpperCase() + "\", \"" + primaryKey + "\");\r\n" +
               "    }\r\n";
    }

    private String getPrimaryKey(final Connection con, final String tablename) throws Exception {
        final String strQuery = "select column_name from user_tab_columns where upper(table_name)=upper(?) and column_name like '%PK'";
        final PreparedStatement pStmt = con.prepareStatement(strQuery);
        pStmt.setString(1, tablename);
        final ResultSet rs = pStmt.executeQuery();
        String columnName = null;
        if(rs.next()) {
            columnName = rs.getString("column_name");
        }
        pStmt.close();

        return columnName;
    }

    private ArrayList getColumns(final Connection con, final String tablename) throws Exception {
        final ArrayList al = new ArrayList();
        final String strQuery = "select column_name, data_type from user_tab_columns where upper(table_name)=upper(?) and column_name not like '%PK' order by data_type";
        final PreparedStatement pStmt = con.prepareStatement(strQuery);
        pStmt.setString(1, tablename);
        final ResultSet rs = pStmt.executeQuery();
        while(rs.next()) {
            al.add(new String[]{rs.getString("column_name"), rs.getString("data_type")});
        }
        pStmt.close();

        return al;
    }

    protected static Connection getConnection() throws Exception {
        final String url = "jdbc:oracle:thin:@192.168.45.6:1521:ora9";
        Class.forName("oracle.jdbc.driver.OracleDriver");
        return DriverManager.getConnection(url, "rei", "rei");
    }
}
